package com.colorlight.cloud.gateway.api.message.constraint.basics;

import com.colorlight.cloud.gateway.api.annotation.name;
import com.colorlight.cloud.gateway.api.annotation.percent;
import com.colorlight.cloud.gateway.api.constants.GatewayConstants;
import com.colorlight.cloud.gateway.api.message.interceptor.trans.TransToProtoInterceptor;
import com.google.protobuf.Message;
import com.google.protobuf.MessageOrBuilder;

import java.lang.reflect.*;
import java.util.*;

import static com.colorlight.cloud.gateway.api.annotation.method.PercentInvoke.percentInvokeToProto;

/**
 * S 为自定义对象 P 为Proto转Java 对象
 * 方法依赖 protobuf的转化
 *
 * @author Demon
 */
public class DefaultTransToProtoRealize {
	private static final List<TransToProtoInterceptor> TRANS_TO_PROTO_INTERCEPTOR_LIST = new ArrayList<>(4);
	
	static {
		TRANS_TO_PROTO_INTERCEPTOR_LIST.add(GatewayConstants.BASE_TRANS_TO_PROTO_INTERCEPTOR);
		TRANS_TO_PROTO_INTERCEPTOR_LIST.add(GatewayConstants.COLLECTION_TRANS_TO_PROTO_INTERCEPTOR);
		TRANS_TO_PROTO_INTERCEPTOR_LIST.add(GatewayConstants.OBJECT_TRANS_TO_PROTO_INTERCEPTOR);
		TRANS_TO_PROTO_INTERCEPTOR_LIST.add(GatewayConstants.ENUM_TRANS_TO_PROTO_INTERCEPTOR);
	}
	
	/**
	 * 通过反射获取 1.基本类型可以实现 2.接口的实现类型 3.枚举类型的实现 4.集合类型的实现
	 *
	 * @param sourceObject 自定义的java对象
	 * @param pClass       类型 需要转化 的目标类型 必须继承 {@link MessageOrBuilder} 可以五十
	 * @return 构建消息对象
	 * @author Demon
	 * @date 18/12/2023/上午9:45
	 */
	public static <J, P extends MessageOrBuilder> Message.Builder objectToProto(
			J sourceObject, Class<P> pClass) {
		
		// 通过反射创建对象
		Message.Builder builder = builderMessageByClass(pClass);
		
		// 获取所有属性: 无视访问权限 所有属性都需要获取
		Field[] sourceFields = sourceObject.getClass().getDeclaredFields();
		for (Field sourceField : sourceFields) {
			// 注解方式处理别名
			name annotation = sourceField.getAnnotation(name.class);
			String name = sourceField.getName();
			if (annotation != null) {
				name = annotation.value();
			}
			//获取当前方法属性
			
			// 设置字段可访问
			sourceField.setAccessible(true);
			// 通过反射 原始对象的属性
			Object value = null;
			try {
				value = sourceField.get(sourceObject);
			} catch (IllegalAccessException e) {
				throw new IllegalArgumentException("");
			}
			//判空处理 空属性 不做处理
			if (value == null) {
				continue;
			}
			Class<?> type = sourceField.getType();
			String finalName = name;
			Optional<TransToProtoInterceptor> first = TRANS_TO_PROTO_INTERCEPTOR_LIST.stream().filter(e -> e.isInterceptor(type)).findFirst();
			TransToProtoInterceptor transToProtoInterceptor = first.orElseThrow(() -> new IllegalArgumentException("未知类型处理" + type.getName()));
			String methodName = transToProtoInterceptor.getMethodName(finalName);
			Class<?> inputClass = transToProtoInterceptor.getInputClass(sourceField);
			if (GatewayConstants.BASE_TRANS_TO_PROTO_INTERCEPTOR.equals(transToProtoInterceptor)) {
				//参数纠正
				percent percent = sourceField.getAnnotation(percent.class);
				if (percent != null) {
					int divisor = percent.divisor();
					value = percentInvokeToProto(value, divisor);
					inputClass = int.class;
				}
			}
			Object finalValue = value;
			transToProtoInterceptor.addAttribute(builder, finalValue, inputClass, methodName);
		}
		return builder;
	}
	
	
	/**
	 * 反射构建对象
	 *
	 * @param pClass proto转java 的实体类
	 * @return 构建 proto转java的实体对象
	 * @author Demon
	 * @date 18/12/2023/下午1:53
	 */
	public static <P extends MessageOrBuilder> Message.Builder builderMessageByClass(Class<P> pClass) {
		// 通过反射创建对象
		Message.Builder builder = null;
		try {
			P p;
			Constructor<P> constructor = pClass.getDeclaredConstructor();
			// 构造方法公有化
			constructor.setAccessible(true);
			p = constructor.newInstance();
			builder = p.getDefaultInstanceForType().toBuilder();
		} catch (InstantiationException
				| IllegalAccessException
				| InvocationTargetException
				| NoSuchMethodException instantiationException) {
			instantiationException.printStackTrace();
		}
		if (Objects.isNull(builder)) {
			throw new IllegalArgumentException("");
		}
		return builder;
	}
	
	/**
	 * 集合处理
	 *
	 * @param field
	 * @return
	 * @author Demon
	 * @date 18/12/2023/下午2:34
	 */
	public static Class<?> CollectionType(Field field) {
		Type genericType = field.getGenericType();
		
		if (genericType instanceof ParameterizedType) {
			ParameterizedType parameterizedType = (ParameterizedType) genericType;
			Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
			
			if (actualTypeArguments.length > 0 && actualTypeArguments[0] instanceof Class) {
				return (Class<?>) actualTypeArguments[0];
			}
		}
		return null;
	}

}
